package com.atguigu.gulimall.product.web;

import com.atguigu.gulimall.product.entity.CategoryEntity;
import com.atguigu.gulimall.product.service.CategoryService;
import com.atguigu.gulimall.product.vo.Catalog2Vo;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author weichen
 * @email 519543138@qq.com
 * @date 2022-09-25 下午2:09
 */
@Controller
public class IndexController {
    @Autowired
    CategoryService categoryService;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    RedisTemplate redisTemplate;

    @GetMapping({"/","/index.html"})
    public String IndexPage(Model model){
        //查出一级分类
        List<CategoryEntity> list= categoryService.getLevel1Category();

        model.addAttribute("categorys",list);
        return "index.html";
    }

    @ResponseBody
    @GetMapping("/index/catalog.json")
    public Map<String,List<Catalog2Vo>> getCatalogJson(){
        return categoryService.getCatalogJson();
    }

    @ResponseBody
    @GetMapping("/hello")
    public String hello(){
        //获取锁，只要锁名字一样，就是同一把锁
        RLock lock = redissonClient.getLock("lock");

        //加锁。
        //阻塞式等待，默认加锁时间30s
        /**
         * 解决了锁的自动续期问题。如果业务时间超长，运行期间(消耗1/3时间程序没执行完)会自动给锁续上新的30s。不担心业务时间长，锁过期
         * 加锁的业务只要完成或者程序死掉，就不会给当前锁续期。即使解锁代码没执行，锁默认30s也会解锁。
         */
        lock.lock();

        /**
         * 10s自动解锁，一定要大于业务时间。
         * 问题：lock.lock(10, TimeUnit.SECONDS);时间到期后，不会自动续期
         * 原理：1、如果我们传了过期时间，就会给redis发送lua脚本，进行占锁。
         * 2、如果我们没传过期时间，就会默认30s，lockWatchdogTimeout看门狗的默认时间
         * 只要占锁成功，就会启动一个定时任务【重新给锁设置过期时间，30s】，每隔lockWatchdogTimeout/3，会自动续期
         */
        lock.lock(10, TimeUnit.SECONDS);

        /**
         * 最佳实战
         * lock.lock(10, TimeUnit.SECONDS); 省掉了自动续期的功能。手动解锁。
         */
        try {
            System.out.println("加锁成功，执行业务"+Thread.currentThread().getId());
            Thread.sleep(30000);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("解锁"+Thread.currentThread().getId());
            lock.unlock();
        }
        return "helloworld";
    }

    /**
     * 读写锁
     * 保证一定能读到最新数据。写锁是排它锁，读锁是共享锁
     * 写锁没释放，读锁就必须等待
     * 读 + 读 ：相当于无锁，并发读，会在redis中记录好，当前的读锁，他们都会加锁成功
     * 写 + 读：等待写锁释放
     * 写 + 写：阻塞方式
     * 读 + 写：有读锁，写也要等待。
     * redis中，读锁是hash存储，
     * @return
     */
    @ResponseBody
    @GetMapping("/write")
    public String write(){
        String s = "";
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.writeLock();
        try {
            rLock.lock();
            System.out.println("写锁加锁成功，执行业务"+Thread.currentThread().getId());
            s = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set("uuid",s);
            Thread.sleep(30000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("写锁解锁"+Thread.currentThread().getId());
            rLock.unlock();
        }
        return s;
    }

    @ResponseBody
    @GetMapping("/read")
    public String read(){
        String s = "";
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.readLock();
        try {
            rLock.lock();
            System.out.println("读锁加锁成功，执行业务"+Thread.currentThread().getId());
            s= (String) redisTemplate.opsForValue().get("uuid");
            Thread.sleep(30000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("读锁解锁"+Thread.currentThread().getId());
            rLock.unlock();
        }
        return s;
    }

    @GetMapping("/lockDoor")
    @ResponseBody
    public String lockDoor() throws InterruptedException {
        RCountDownLatch lockDoor = redissonClient.getCountDownLatch("lockDoor");
        lockDoor.trySetCount(5);
        lockDoor.await();
        return "全部关门";
    }

    @GetMapping("/gogo/{id}")
    @ResponseBody
    public String gogo(@PathVariable Long id) throws InterruptedException {
        RCountDownLatch lockDoor = redissonClient.getCountDownLatch("lockDoor");
        lockDoor.countDown();
        return id+"走了";
    }

}
